#!/usr/bin/env node
//-*- mode:js -*-

(() => {
  'use strict'

  let LISP

  // Run stream.
  const runStream = (stream, compile) => {
    let result
    for (;;) {
      const s = LISP.read(stream)
      if (s == null)
        return result

      if (compile) {
        result = LISP.compile(s)
        LISP.print(result + ';\n')
      } else {
        result = LISP.eval(s)
      }
    }
  }

  // Run codes.
  const runCodes = (codes, compile) => runStream(new LISP.StrStream(codes), compile)

  // Read-Eval-Print loop.
  const repl = () => {
    const readline = require('readline')
    const rl = readline.createInterface(process.stdin, process.stdout)

    process.stdin.resume()
    process.stdin.setEncoding('utf8')

    const prompt = '> '
    const prompt2 = '. '
    const inputs = []

    rl.setPrompt(prompt)
    rl.prompt()
    rl.on('line', (line) => {
      inputs.push(line)
      try {
        const code = inputs.join('\n')
        if (code.match(/^\s*$/)) {
          inputs.length = 0
        } else {
          const result = runCodes(code, compileOnly)
          // Otherwise input should be consumed.
          inputs.length = 0
          console.log(LISP['x->string'](result, 10))
        }
        rl.setPrompt(prompt)
        rl.prompt()
      } catch (e) {
        if (e instanceof LISP.NoCloseParenException) {
          // In REPL, if NoCloseParenException occurs,
          // a user keep typing so inputs should be kept.
          rl.setPrompt(prompt2)
        } else {
          console.error(e)
          inputs.length = 0
          rl.setPrompt(prompt)
        }
        rl.prompt()
      }
    }).on('close', () => {
      if (inputs.length > 0) {
        console.error('Input not terminated: [' + inputs + ']')
        process.exit(1)
        return
      }
      process.exit(0)
    })
  }
  const replNoPrompt = () => {
    process.stdin.resume()
    process.stdin.setEncoding('utf8')
    return LISP.load(LISP['*stdin*'])
  }

  // Run script.
  const fs = require('fs')

  // Main.
  let translator = null
  let index = 2
  let compileOnly = false
  for (; index < process.argv.length; ++index) {
    const option = process.argv[index]
    if (option == '-c') {
      compileOnly = true
      continue
    }
    if (option == '-t') {
      translator = process.argv[++index]
      continue
    }
    break
  }

  if (translator == null) {
    try {
      if (fs.statSync(__dirname + '/lisp2js.js').isFile())
        translator = __dirname + '/lisp2js'
    } catch (e) {
      translator = __dirname + '/lisp2js.min'
    }
  }
  LISP = require(translator)

  if (index >= process.argv.length) {  // No input file name: read from stdin.
    const tty = require('tty')
    if (tty.isatty(0))
      return repl()
    if (!compileOnly)
      return replNoPrompt()

    process.stdin.resume()
    process.stdin.setEncoding('utf8')
    return runStream(LISP['*stdin*'], compileOnly)
  }

  // Process command line argument as a script file.
  try {
    LISP['*argv*'] = LISP['vector->list'](process.argv.slice(index + 1))
    let status = 0
    if (compileOnly) {
      const text = fs.readFileSync(process.argv[index], 'utf-8')
      const matchShebang = text.match(/^#!(.*)\n/)
      if (matchShebang)
        text = text.slice(matchShebang[0].length)
      runCodes(text, compileOnly)
    } else {
      LISP.load(process.argv[index])
      if ('main' in LISP)
        status = LISP.main(LISP.cons(process.argv[index], LISP['*argv*']))
    }
    if (status)
      process.exit(status)
  } catch (e) {
    process.stderr.write(e.toString() + '\n')
    process.exit(1)
  }
})()
